%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Industrial Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% read2DArrayFromFile.m
% getSizeOf2DArrayFromFile.m
% writeToLogFile.m
%
%==========================================================================

%==========================================================================
% The function "combine2DArraysFromFiles" reads a number of text files
% holding 2D arrays with the same number of rows and writes their
% horizontal concatenations to a single text file.
% INPUT: "fileNameCellArr" is a cell column vector whose cells are strings
% specifying the names (including a full directory path) of the text files
% to be read and combined.
% OUTPUT: "outFileName" returns a string holding the name of the output
% text file where all signals are stored.
%==========================================================================
function outFileName = combine2DArraysFromFiles(fileNameCellArr)

% Get the number of files.
numFiles = numel(fileNameCellArr);

% If there is only one file then there is nothing to be done.
if (numFiles == 1)
    outFileName = fileNameCellArr{1};
    return;
end

% Read in the signals from the first file.
allSignalsArr = read2DArrayFromFile(fileNameCellArr{1});

% Get the length of the signals in the first file and their number.
[signalLength, numSignals] = size(allSignalsArr);

% Accumulate the number of signals in all files and check signal lengths
% for consistency.
for k = 2:numFiles
    
    % Get the length of the signals in the current file and their number.
    [currSignalLength, currNumSignals] = getSizeOf2DArrayFromFile(fileNameCellArr{k});
    
    % Assert that the signals in the current file are of the same length as
    % those in the first file.
    assert((currSignalLength == signalLength), ...
        'SoundRestoration:combine2DArraysFromFiles:DifferentSignalLength', ...
        'Signals in files %s and %s have different lengths.', ...
        fileNameCellArr{1}, fileNameCellArr{k});
    
    % Accumulate the number of signals in all files.
    numSignals = numSignals + currNumSignals;
end 

% Find all instances of substrings of the form ['Im', any digits, a dash]
% in the first file name.
[startArr, endArr] = regexpi(fileNameCellArr{1}, 'Im[0-9]+-');
% Assert that the signals in the current file are of the same length as
% those in the first file.
assert(~isempty(startArr), ...
    'SoundRestoration:combine2DArraysFromFiles:MissingPatternInFirstFileName', ...
    ['The regular expression ''Im[0-9]+-'' is not found in the file name %s.\n' ...
    'It needs to be present in the name of the first of the files to be combined.'], ...
    fileNameCellArr{1});

outFileNamePrefix = fileNameCellArr{1}(1:(startArr(end) - 1));
firstFileIndStr = fileNameCellArr{1}((startArr(end) + 2):(endArr(end) - 1));

% Find all instances of substrings of the form ['Im', any digits, a dash]
% in the last file name.
[startArr, endArr] = regexpi(fileNameCellArr{end}, 'Im[0-9]+-[0-9]+');
% Assert that the signals in the current file are of the same length as
% those in the first file.
assert(~isempty(startArr), ...
    'SoundRestoration:combine2DArraysFromFiles:MissingPatternInLastFileName', ...
    ['The regular expression ''Im[0-9]+-[0-9]+'' is not found in the file name %s.\n' ...
    'It needs to be present in the name of the last of the files to be combined.'], ...
    fileNameCellArr{end});

tempStr = fileNameCellArr{end}(startArr(end):endArr(end));
startArr = regexpi(tempStr, '-');
lastFileIndStr = tempStr((startArr(end) + 1):end);
% Create a name for the output file.
outFileName = [outFileNamePrefix 'Im' firstFileIndStr '-' lastFileIndStr '.txt'];

% Keep repeating the writing process until the data read from the combined
% file agrees with the data from the individual files but no more than 5
% times.
numAttempts = 5;
for currAttempt = 1:numAttempts
    
    if (currAttempt > 1)
        writeToLogFile(['Warning in combine2DArraysFromFiles: Starting attempt ' ...
            num2str(currAttempt) ' of ' num2str(numAttempts) ...
            ' in combining sound signal text files.']);
    end
    
    % Open the output text file for writing.
    fid = fopen(outFileName, 'w');
    if (fid == -1)
        writeToLogFile(['Warning in combine2DArraysFromFiles: Unable to open the file ' outFileName ' for writing.']);
        return;
    end
    
    % Write the two header rows.
    fprintf(fid, 'number of rows: %d\n', signalLength);
    fprintf(fid, 'number of columns: %d\n', numSignals);
    
    % Write the signals from all files.
    for k = 1:numFiles
        
        writeToLogFile(['Copying file ' num2str(k) ' of ' num2str(numFiles) ' in combining sound signal text files.\n']);
        fprintf(1, ['Copying file ' num2str(k) ' of ' num2str(numFiles) ' in combining sound signal text files.\n']);
        
        % Read in the signals from the current file.
        allSignalsArr = read2DArrayFromFile(fileNameCellArr{k});
        
        % Write the sound signals.
        fprintf(fid, '%.10f\n', allSignalsArr);
        
    end
    
    % Close the file.
    fclose(fid);
    
    
    % Read in the text file just written.
    read2DArr = read2DArrayFromFile(outFileName);
    
    checkFailed = false;
    % Compare the read data to the written data.
    if ( (numel(size(read2DArr)) ~= 2)|| ...
            (size(read2DArr, 1) ~= signalLength)|| ...
            (size(read2DArr, 2) ~= numSignals) )
        checkFailed = true;
    end
    
    if (checkFailed == false)
        
        % Check the data from the individual files.
        prevPos = 0;
        for k = 1:numFiles
            
            writeToLogFile(['Checking file ' num2str(k) ' of ' num2str(numFiles) ' in combining sound signal text files.\n']);
            fprintf(1, ['Checking file ' num2str(k) ' of ' num2str(numFiles) ' in combining sound signal text files.\n']);
            
            % Read in the signals from the current file.
            allSignalsArr = read2DArrayFromFile(fileNameCellArr{k});
            
            % Check the data from the current file.
            portionArr = read2DArr((prevPos + 1):(prevPos + numel(allSignalsArr)));
            if (nnz(abs(portionArr(:) - allSignalsArr(:)) > 1.0e-010) > 0)
                checkFailed = true;
                break;
            end
            prevPos = prevPos + numel(allSignalsArr);
        end
    end
    
    % If everything matches, then break. Otherwise, continue.
    if (checkFailed == false)
        writeToLogFile(['sound signal text files combined successfully in attempt: ' num2str(currAttempt)]);
        break;
    else
        continue;
    end
end

% If the file was never written successfully, then issue an error.
assert((checkFailed == false), ...
    'SoundRestoration:combine2DArraysFromFiles:FailedToCombine', ...
    'Text files failed to combine in %d attempts.', numAttempts);

return;
% end of the function "combine2DArraysFromFiles"
